/**
 * @description 常用类型定义: **批注信息**
 * @typedef  {object} Annotation
 * @property {string} name   - 批注名称 唯一标识
 * @property {string} image  - 批注图片base64编码
 * @property {number} page   - 批注插入的页码
 * @property {number} width  - 批注图片宽度
 * @property {number} height - 批注图片宽度
 * @property {number} cx     - 批注图片相对页面左上角的x坐标
 * @property {number} cy     - 批注图片相对页面左上角的y坐标
 */

/**
 * @description 常用类型定义: **右键菜单类型**，枚举， 不同厂家菜单名称有可能不一样，需要做映射
 * @typedef {
 * 'stamp' |
 * 'handtool_tablet' |
 * 'handtool_eraser' |
 * 'handtool_freetext' |
 * 'handtool_delAllAnnot'
 * } ContextMenuEnum
 * @description stamp 橡皮图章
 * @description handtool_tablet 手写签批
 * @description handtool_eraser 橡皮擦
 * @description handtool_freetext 文本框工具
 * @description handtool_delAllAnnot 清空全部注释
 */

/**
 * @description 常用类型定义: **工具栏菜单类型**，枚举，不同厂家工具栏菜单名称有可能不一样，需要做映射
 * @typedef {
 * 'navigator' |
 * 'toolbar' |
 * 'tb_annot' |
 * 'tb_security' |
 * 'e_preference' |
 * 'f_open' |
 * 'f_save' |
 * 'f_saveas' |
 * 'f_print'
 * } MenuBarEnum
 * @description navigator 导航
 * @description toolbar 工具
 * @description tb_annot 批注
 * @description tb_security 安全
 * @description e_preference 首选项
 * @description f_open 打开文件
 * @description f_save 保存文件
 * @description f_saveas 另存为
 * @description f_print 打印
 */

const Constant = {
    CLSID: 'C7F277DC-6C47-AB2C-FB6A-070DC8BE7533',
    // 嵌入类型
    EMBED_TYPE: 'application/x-suwell',
}

let installed = false
class SuwellOfdReader {
    static installed() {
        // 已经判断过，无需重新判断
        if (installed) return installed
        const div = document.createElement('div')
        document.body.appendChild(div)
        const id = Math.random()
        div.innerHTML = `<embed 
            id="${id}"
            width="1px"
            height="1px"
            type="${Constant.EMBED_TYPE}"
            object_id="${id}"
            inited_call="__OFD_OCX_Ready"
        >`
        const ocx = document.getElementById(id)
        const version = ocx.version ? ocx.version() : null
        if (version) {
            console.log(version)
            ocx.exit()
        }
        installed = version != null
        document.body.removeChild(div)
        return installed
    }

    /**
     * 插件初始化
     * @param {object} options 插件配置
     * @param {string} options.id 插件容器dom ID
     * @param {string} options.width 插件容器宽度
     * @param {string} options.height 插件容器高度
     * @param {'npapi' | 'websocket'} options.loadType 加载方式 默认 'npapi' 方式
     */
    constructor(options) {
        const container = document.getElementById(options.id)
        if (!container) {
            console.warn('请指定div容器')
            return
        }
        this.containerId = options.id
        this.id = Math.random()
        container.innerHTML = `
        <embed 
            id="${this.id}"
            width="${options.width || '100%'}"
            height="${options.height || '100%'}"
            type="${Constant.EMBED_TYPE}"
            object_id="${this.id}"
            inited_call="__OFD_OCX_Ready"
        >`
        this.ocx = document.getElementById(this.id)
        this.userId = null
        this.userName = null
    }

    /**
     * 设置用户信息
     * @param {{ userId: string, userName: string }} userInfo
     */
    setUserInfo(userInfo) {
        if (!this.ocx) return
        const ocx = this.ocx
        this.userName = userInfo.userName
        this.userId = userInfo.userId
        ocx.setUserName(userInfo.userName)
        ocx.setUserID(userInfo.userId)
        ocx.setDataTag('')
    }

    /**
     * 设置网络接口请求头，用于下载文件，上传文件时，带上授权信息以通过后端服务的接口授权
     * @param {string} name 请求头字段名
     * @param {string} value 值
     */
    setRequestHeader(name, value) {
        if (!this.ocx) return
        const ocx = this.ocx
        if (name === 'Cookie') {
            ocx.setConfigInfo('userinfo.usertoken', value)
        }
    }

    /**
     * 设置文件下载超时
     * @param {number} timeout 毫秒数
     */
    setDownloadTimeout(timeout) {
        if (!this.ocx) return
        const ocx = this.ocx
        if (!Number.isNaN(timeout)) {
            ocx.setConfigInfo('downloadurl.timeout', timeout + '')
        }
    }

    /**
     * 设置页面缩放比例
     * @param {number} ratio  100 为原始比例，数值越大，放大倍数越大
     */
    setZoomRatio(ratio) {
        if (!this.ocx) return
        const ocx = this.ocx
        ocx.setScale(ratio)
    }

    /**
     * 打开文件 返回成功或失败, 成功: resolve, 失败-reject
     * @param {string} filePath  文件路径
     * @param {boolean} readonly 是否只读
     * @return {Promise<void>}
     */
    async openFile(filePath, readonly) {
        if (!this.ocx) return Promise.reject('ofd插件未初始化！')
        const ocx = this.ocx
        return new Promise((resolve, reject) => {
            const openFileCbName = 'suwellcb_open_file_' + this.containerId
            window[openFileCbName] = (info) => {
                const res = JSON.parse(info)
                console.log('[suwell_openFile]:', res)
                if (res.operateresult === '1') {
                    ocx.performClick('vzmode_original')
                    // 设置橡皮图章只能按比例放大缩小
                    ocx.SetNotDrawdragControl('leftdrag,topdrag,rightdrag,bottomdrag')
                    ocx.SetKeepStampRatioParam(true, 'auto', 'auto')
                    resolve()
                } else {
                    reject()
                }
            }
            ocx.registListener('f_open', openFileCbName, true)
            ocx.openFile2(filePath, readonly)
        })
    }

    /**
     * 保存文件 返回成功或失败, 成功: resolve, 失败-reject
     * 客户端上传文件的字段名是 source, 需要后端接口处理
     * 客户端上传的文件名是uuid，需要额外传参处理文件名
     * @param {string} savePath 保存文件的路径，一般是后端的上传文件接口
     * @return {Promise<void>}
     */
    async saveFile(savePath) {
        if (!this.ocx) return Promise.reject('ofd插件未初始化！')
        const ocx = this.ocx
        return new Promise((resolve, reject) => {
            const saveFileCbName = 'suwellcb_save_file_' + this.containerId
            window[saveFileCbName] = params => {
                try {
                    params = JSON.parse(params || null)
                    console.log(params)
                    if (/true/.test(params.result) && !/code:5\d+/.test(params.responsecode)) {
                        console.timeEnd('suwell_saveFile')
                        resolve(params)
                    } else {
                        reject(params)
                    }
                } catch (err) {
                    console.log('suwellcb_saveurlPerformed err', err)
                    console.timeEnd('suwell_saveFile')
                    reject(err)
                }
            }
            ocx.registListener('f_saveurl', saveFileCbName, true)
            console.time('suwell_saveFile')
            ocx.saveFile(savePath)
        })
    }

    /**
     * 添加批注，即在版式文件中，添加橡皮图章
     * @param {Annotation} annotation
     * @return {Promise<string>} 添加成功返回 节点名称标识
     */
    addAnnotation(annotation) {
        if (!this.ocx) return Promise.reject('ofd插件未初始化！')
        const ocx = this.ocx
        const json = {
            ...annotation,
            name: 'stampAnnot_' + this.uuid(),
            applysign: 'false',
            origin: 'top-left',
            pages: Math.min(annotation.page, this.getPageCount()),
        }
        console.log('addStampAnnot', json)
        ocx.setDataTag(json.name)
        ocx.addStampAnnot(JSON.stringify(json))
        ocx.setDataTag('')

        const exist = ocx.isStampAnnotExist(json.name)
        if (exist) {
            return Promise.resolve(json.name)
        }
        return Promise.reject('添加批注失败')
    }

    /**
     * 删除批注 成功: resolve, 失败-reject
     * @param {string} name 批注名称
     * @return {Promise<void>}
     */
    async deleteAnnotation(name) {
        if (!this.ocx) return Promise.reject('ofd插件未初始化！')
        const ocx = this.ocx
        const originLength = this.getAnnotations().length
        ocx.deleteAnnotationByParameter(JSON.stringify({
            userid: this.userId,
            datatag: name,
            annotetype: 'Stamp',
        }))
        const newLength = this.getAnnotations().length
        if (newLength === originLength - 1) {
            return Promise.resolve()
        } else {
            return Promise.reject(false)
        }
    }

    /**
     * 获取批注列表, 获取所有批注列表-橡皮图章
     * @return {Array<Annotation>}
     */
    getAnnotations() {
        if (!this.ocx) return []
        const ocx = this.ocx
        const annotInfo = ocx.getAnnotInfoBy(JSON.stringify({
            annottype: 'Stamp',
        }))
        try {
            const list = annotInfo ? JSON.parse(annotInfo) : []
            console.log('origin data:', list)
            return list.map(item => {
                const json = JSON.parse(ocx.getFreeTextInfoById(item.annotid))
                console.log(json)
                /**
                 * @type Annotation
                 */
                return {
                    cx: Number(json['pos-x']),
                    cy: Number(json['pos-y']),
                    height: Number(json.height),
                    width: Number(json.width),
                    page: Number(json['page-index']),
                    name: String(json.datatag),
                    image: '',
                }
            })
        } catch (e) {
            return []
        }
    }

    /**
     * 获取矩形区域内的批注列表
     * @param {object} area 矩形区域对象
     * @param {number} area.page    目标页面
     * @param {number} area.width   矩形区域宽度
     * @param {number} area.height  矩形区域高度
     * @param {number} area.cx      矩形区域相对页面左上角的x坐标
     * @param {number} area.cy      矩形区域相对页面左上角的y坐标
     * @return {Array<Annotation>}
     */
    getAnnotationsByArea(area) {
        if (!this.ocx) return []
        const ocx = this.ocx
        const info = ocx.getAnnotIdsByInfo(JSON.stringify({
            'pos-x': area.cx,
            'pos-y': area.cy,
            'page-index': Math.min(area.page, this.getPageCount()),
            width: area.width,
            height: area.height,
        }))
        const posList = []
        try {
            const ids = JSON.parse(info).annotids.split(',')
            ids.forEach(id => {
                if (id) {
                    const json = JSON.parse(ocx.getFreeTextInfoById(id))
                    /**
                     * @type Annotation
                     */
                    const target = {
                        cx: Number(json['pos-x']),
                        cy: Number(json['pos-y']),
                        height: Number(json.height),
                        width: Number(json.width),
                        page: Number(json['page-index']),
                        name: String(json.datatag),
                        image: '',
                    }
                    posList.push(target)
                }
            })
            return posList
        } catch (e) {
            return posList
        }
    }

    /**
     * 获取base64格式的文件数据
     */
    getBase64Data() {
        if (!this.ocx) return null
        const ocx = this.ocx
        return ocx.saveBase64File()
    }

    /**
     * 获取文件页数
     * @return {number}
     */
    getPageCount() {
        if (!this.ocx) return 0
        const ocx = this.ocx
        return ocx.getPageCount()
    }

    /**
     * 跳转到指定位置，一般是用于添加批注时，页面聚焦于批注的位置
     * @param {number} page 页码
     * @param {number} cx   页面左上角的x坐标
     * @param {number} cy   页面左上角的y坐标
     */
    gotoDest(page, cx, cy) {
        if (!this.ocx) return
        this.ocx.gotoDest(page, cx, cy)
    }

    /**
     * 页面缩放
     * @param {'in' | 'out'} type in - 放大，out - 缩小
     */
    zoom(type) {
        if (!this.ocx) return
        const ocx = this.ocx
        if (type === 'in') {
            ocx.performClick('v_zoomin')
        } else if (type === 'out') {
            ocx.performClick('v_zoomout')
        }
    }

    /**
     * 设置右键菜单， 建议在打开文件后调用
     * @param {Array<ContextMenuEnum>} menus 需要控制的右键菜单数组
     * @param {boolean} visible  是否显示右键菜单
     */
    setContextMenuVisible(menus, visible) {
        if (!this.ocx) return
        const ocx = this.ocx
        if (Array.isArray(menus) && menus.length) {
            menus.forEach(item => {
                ocx.SetContextMenuStripVisible(item, visible)
            })
        }
    }

    /**
     * 设置工具栏组件是否可见， 建议在打开文件后调用
     * @param {Array<MenuBarEnum>} items 工具菜单元素名称数组
     * @param {boolean} visible 是否可见
     */
    setCompositeVisible(items, visible) {
        if (!this.ocx) return
        const ocx = this.ocx
        if (Array.isArray(items) && items.length) {
            items.forEach(item => {
                ocx.setCompsiteVisible(item, visible)
            })
        }
    }

    /**
     * 打印文件
     * @param {boolean} isGrayPrint 是否灰度打印
     */
    printFile(isGrayPrint) {
        if (!this.ocx) return
        const ocx = this.ocx
        const printCbName = `cb_print_${this.containerId}`
        window[printCbName] = params => {
            try {
                params = JSON.parse(params || null)
            } catch (err) {
                console.log('suwellcb_print err', err)
            }
            console.log('suwellcb_print', params)
            if (params.isok === 'true') {
                this.$emit('afterPrint', params)
            } else {
                // this.$message.error('打印出错')
            }
        }
        ocx.registListener('f_print', printCbName, true)
        ocx.setPrintCompsiteChecked('print_gray', isGrayPrint) // 设置灰度打印是否勾选
        ocx.setPrintCompsiteEnable('print_gray', !isGrayPrint) // 设置灰度打印是否可改变
        ocx.setPrintCompsiteChecked('fastprint', false) // 禁用快速打印
        ocx.setPrintCompsiteEnable('fastprint', false) // 禁用快速打印
        ocx.performClick('f_print')
    }

    /**
     * 下载灰度文件
     * @param {string} fileName 需要下载保存的文件名
     */
    downloadGrayFile(fileName) {
        if (!this.ocx) return
        const ocx = this.ocx
        ocx.setConfigInfo('signature.callcancelseal', false)
        ocx.decryptAllSeals(true, true, true, fileName)
    }

    /**
     * 盖章操作
     */
    signSeal() {
        if (!this.ocx) return
        const ocx = this.ocx
        ocx.performClick('t_sealsign')
    }

    /**
     * 模拟点击
     * @param {string} command 需要执行的命令
     */
    performClick(command) {
        if (!this.ocx) return
        const ocx = this.ocx
        ocx.performClick(command)
    }

    /**
     * 关闭文件
     */
    closeFile() {
        if (!this.ocx) return
        this.ocx.closeFile()
    }

    /**
     * 插件销毁，执行清理工作
     */
    destroy() {
        if (!this.ocx) return
        this.ocx.exit()
        document.getElementById(this.containerId).removeChild(this.ocx)
        this.ocx = null
    }
}

export default SuwellOfdReader
